package com.qiudao.vaildator.aspect;


import com.qiudao.commonresult.share.result.Result;
import com.qiudao.commonresult.share.result.ResultSupport;
import com.qiudao.commonresult.share.util.ResultUtil;
import com.qiudao.vaildator.annotation.Check;
import com.qiudao.vaildator.annotation.Validator;
import com.qiudao.vaildator.enums.ErrorResult;
import com.qiudao.vaildator.exception.ValidatorException;
import com.qiudao.vaildator.validator.abstracts.ValidatorAdapter;
import net.sf.oval.ConstraintViolation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.LoggerFactory;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.stereotype.Service;

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Description: 接口验证类
 * @author: gdc
 * @date: 2019/8/31
 * @version 1.0
 */
@Service
@Aspect
public class ValidatorAspect {

    private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(ValidatorAspect.class);

    private static final LocalVariableTableParameterNameDiscoverer PARAMETER_NAME_DISCOVERER =
            new LocalVariableTableParameterNameDiscoverer();

    private static final net.sf.oval.Validator VALIDATOR = new net.sf.oval.Validator();

    private static final int MAP_INIT_SIZE = 16;

    /**
     * 缓存验证适配器类，防止ADAPTER堆积
     */
    private static final ConcurrentHashMap<String, ValidatorAdapter> VALIDATOR_ADAPTER_MAP = new ConcurrentHashMap<>(16);

    /**
     * 拦截所有使用Validator注解的接口，并进行参数合法有效性验证
     *
     * @param joinPoint Validator注解切面
     * @return Object 目标方法的返回值
     * @throws Throwable 调用目标方法将可能抛出异常
     */
    @Around("@annotation(com.qiudao.vaildator.annotation.Validator)")
    public Object validator(ProceedingJoinPoint joinPoint) throws Throwable {
        /*
        如果 spring aop 配置为 <aop:aspectj-autoproxy /> 或者 <aop:aspectj-autoproxy proxy-target-class="false"/>，
        则使用JDK基于接口的代理，那么method为interface的签名方法；

        如果配置为 <aop:aspectj-autoproxy proxy-target-class="true" />，
        则使用cglib基于类的代理，那么method为interface的实现类方法
         */
        final Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();

        //如果method为interface的签名方法，则获取其实现方法
        final Method implMethod = joinPoint.getTarget().getClass().getMethod(method.getName(), method.getParameterTypes());

        //参数值数组
        final Object[] objects = joinPoint.getArgs();

        final Map<String, Integer> paramMap = new HashMap<>(MAP_INIT_SIZE);

        //获取参数名称数组，getParameterNames的入参不能是方法签名
        final String[] params = PARAMETER_NAME_DISCOVERER.getParameterNames(implMethod);

        //生成方法参数名索引，用于从objects中取得对应参数的值
        final int paramLen = params.length;
        for (int i = 0; i < paramLen; i++) {
            paramMap.put(params[i], i);
        }

        //取得需要验证的注解对象
        Validator validator = method.getAnnotation(Validator.class);

        //如果使用jdk代理，而@Validator写在实现层，则获取实现层Validator
        if (validator == null || validator.value().length < 1) {
            validator = implMethod.getAnnotation(Validator.class);
            //如果使用cglib，而@Validator写在interface上，则需要获取method的签名方法
            if (validator == null || validator.value().length < 1) {
                final Class<?>[] interfaces = joinPoint.getTarget().getClass().getInterfaces();
                if (interfaces.length < 1) {
                    return joinPoint.proceed();
                }
                for (Class<?> inter : interfaces) {
                    Method interfaceMethod;
                    try {
                        interfaceMethod = inter.getMethod(method.getName(), method.getParameterTypes());
                    } catch (Exception ignored) {
                        continue;
                    }
                    validator = interfaceMethod.getAnnotation(Validator.class);
                    if (validator != null) {
                        break;
                    }
                }
                if (validator == null || validator.value().length < 1) {
                    return joinPoint.proceed();
                }
            }
        }

        //取得明细验证规则
        final Check[] checks = validator.value();
        for (final Check p : checks) {
            Integer idx = paramMap.get(p.name());
            //如@Check指定的name名称找不到对应参数，则跳过该check校验并输出日志信息
            if (idx == null || idx >= objects.length) {
                LOGGER.warn("Validation skipped, the check name:{} can not find an param in the method:[{}]",
                        p.name(), this.joinMethodInfo(implMethod));
                continue;
            }
            final Object o = objects[idx];
            final Class<? extends ValidatorAdapter> vda = p.adapter();

            //如未指定适配器，则默认使用oval验证对象
            if (vda.getName().equals(ValidatorAdapter.class.getName())) {
                if (o != null) {
                    //当验证对象不为null，使用oval验证框架验证
                    if (o instanceof Collection) {
                        //验证参数为list时，每个VO单独处理
                        for (Object o1 : ((Collection) o)) {
                            List<ConstraintViolation> violations = VALIDATOR.validate(o1);
                            if (violations != null && violations.size() > 0) {
                                ConstraintViolation constraintViolation = violations.get(0);
                                if (Result.class.equals(implMethod.getReturnType())) {
                                    return new ResultSupport(constraintViolation.getErrorCode(), constraintViolation.getMessage());
                                }
                            }
                        }
                    } else {
                        List<ConstraintViolation> violations = VALIDATOR.validate(o);
                        if (violations != null && violations.size() > 0) {
                            ConstraintViolation constraintViolation = violations.get(0);
                            return new ResultSupport<>(constraintViolation.getErrorCode(), constraintViolation.getMessage());
                        }
                    }
                }
            } else {
                ValidatorAdapter adapter = VALIDATOR_ADAPTER_MAP.get(vda.getName());
                if (adapter == null) {
                    ValidatorAdapter adapterInstance = (ValidatorAdapter) Class.forName(vda.getName()).newInstance();
                    adapter = VALIDATOR_ADAPTER_MAP.putIfAbsent(vda.getName(), adapterInstance);
                    //处理并发情况
                    if (adapter == null) {
                        adapter = adapterInstance;
                    }
                }

                //验证结果 true:验证通过，false:验证不通过
                boolean flag;

                //不需要的传验证参数的，屏蔽调v和c等验证参数
                if (ValidatorAdapter.TYPE_OBJECT == adapter.getValidateType()) {
                    flag = adapter.validate(o);
                }
                //其他的必须传v的验证
                else if (ValidatorAdapter.TYPE_VALUE == adapter.getValidateType()) {
                    if (p.v().length != 0) {
                        flag = adapter.validate(o, p.v());
                    } else {
                        return new ResultSupport(ErrorResult.CHECK_VALUE.getCode(), ErrorResult.CHECK_VALUE.getMessage());
                    }
                }
                //枚举类型验证，传v的以v中的为主，未设置v以枚举类c为验证参数
                else if (ValidatorAdapter.TYPE_ENUM == adapter.getValidateType()) {
                    if (p.v().length == 0 && p.c() != Enum.class) {
                        flag = adapter.validate(o, p.c());
                    } else if (p.v().length != 0) {
                        flag = adapter.validate(o, p.v());
                    } else {
                        return new ResultSupport(ErrorResult.CHECK_VALUE.getCode(), ErrorResult.CHECK_VALUE.getMessage());
                    }
                }
                //传入的adapter未实现对应验证规则的报错
                else {
                    return new ResultSupport(ErrorResult.CHECK_RULE.getCode(), ErrorResult.CHECK_RULE.getMessage());
                }

                if (!flag) {
                    String message = isBlank(p.message()) ? adapter.getMessage() : p.message();
                    return processResult(p.name(), p.errorCode(), p.i18nCode(), message, implMethod.getReturnType());
                }
            }
        }
        //调用目标方法
        return joinPoint.proceed();
    }

    private boolean isBlank(String str) {
        int strLen;
        if (str == null || (strLen = str.length()) == 0) {
            return true;
        }
        for (int i = 0; i < strLen; i++) {
            if ((!Character.isWhitespace(str.charAt(i)))) {
                return false;
            }
        }
        return true;
    }

    private String joinMethodInfo(final Method method) {
        return method.getDeclaringClass().getName() + "#" + method.getName()
                + "(" + Stream.of(method.getParameterTypes()).map(Class::getName).collect(Collectors.joining(",")) + ")";
    }

    private Result processResult(String fieldName, String errorCode, String i18nCode, String message, Class classType) {
        if (Result.class.equals(classType)) {
            if (this.isBlank(i18nCode)) {
                return ResultUtil.failResult(errorCode, fieldName + ": " + message);
            }
            return ResultUtil.failResult(errorCode, i18nCode, fieldName + ": " + message);
        }
        throw new ValidatorException(errorCode, fieldName + ": " + message);
    }
}